#include "../util/structure.h"
#include "optimize.h"

static void init(function * a, bool is_forward, boundary_func f){

    FOR_LIST(iter,a->BBlist){
        BB * now=LIST_GET(iter,BB *);
        MAP_CLEAR(now->mp1);
        MAP_CLEAR(now->mp2);
        if(iter==a->BBlist.begin&&is_forward) now->mp1=f(a->param);
        else now->mp1=new_map();
        if(iter==a->BBlist.end&&!is_forward) now->mp2=f(a->param);
        else now->mp2=new_map();
    }
}

static void build_map(function * a,bool is_forward, merge_func merge, statement_func state){
    bool ret=true;
    while(ret){
        ret=false;
        if(is_forward){
            FOR_LIST(iter,a->BBlist){
                BB * now=LIST_GET(iter,BB *);
                if(a->BBlist.begin!=iter&&LIST_EMPTY(now->entry)) continue;
                FOR_LIST(iter2,now->entry) 
                    if(merge(now->mp1,LIST_GET(iter2,BB *)->mp2)) ret=true;
                map_cpy(now->mp1,now->mp2);
                FOR_LIST(iter2,now->statement)
                    state(now->mp2,LIST_GET(iter2,operation *));
            }
        }else{
            FOR_LIST_BACK(iter,a->BBlist){
                BB * now=LIST_GET(iter,BB *);
                if(a->BBlist.begin!=iter&&LIST_EMPTY(now->entry)) continue;
                FOR_LIST(iter2,now->exit)
                    if(merge(now->mp2,LIST_GET(iter2, BB*)->mp1)) ret=true;
                map_cpy(now->mp2,now->mp1);
                FOR_LIST_BACK(iter2,now->statement)
                    state(now->mp1,LIST_GET(iter2,operation *));
            }
        }
    }
}

static void optimize_work(function * a,bool is_forward,statement_func state,optimize_func opt){
    if(is_forward) FOR_LIST(iter,a->BBlist){
        BB * now=LIST_GET(iter,BB *);
        if(a->BBlist.begin!=iter&&LIST_EMPTY(now->entry)) continue;
        list nxt=NULL;
        FOR_LIST_2(iter2,now->statement,nxt){
            nxt=iter2->nxt;
            operation * o=LIST_GET(iter2,operation *);
            if(!opt(now->mp1,o)) {
                FREE_OPERATION(o);
                LIST_DELETE(now->statement,iter2);
            }else state(now->mp1,o);
        }
    }else FOR_LIST_BACK(iter,a->BBlist){
        BB * now=LIST_GET(iter,BB *);
        if(a->BBlist.begin!=iter&&LIST_EMPTY(now->entry)) continue;
        list prev=NULL;
        FOR_LIST_BACK_2(iter2,now->statement,prev){
            prev=iter2->prev;
            operation * o=LIST_GET(iter2,operation *);
            if((prev&&!active_opt2state(now->mp2,o,LIST_GET(prev,operation *)))||!opt(now->mp2,o)){
                FREE_OPERATION(o);
                LIST_DELETE(now->statement,iter2);
            }else state(now->mp2,o);
        }
    }
}

struct{
    bool is_forward;
    boundary_func boundary;
    merge_func merge;
    statement_func statement;
    optimize_func optimize;
} table[4] ={
    {true,const_boundary_init,const_merge,const_statement,const_optimize},
    {true,common_boundary_init,common_merge,common_statement,common_optimize},
    {true,copy_boundary_init,copy_merge,copy_statement,copy_optimize},
    {false,active_boundary_init,active_merge,active_statement,active_optimize}
};

extern BB* label_entry[20000];
static void BB_dfs(BB * now){
    BB * nxt=now->nxt0;
    operation * last=NULL;
    if(now->statement.end) last=LIST_GET(now->statement.end, operation *);
    if((!last||(last->op!=OP_RET&&last->op!=OP_GOTO))&&nxt){
        bool tmp=LIST_EMPTY(nxt->entry);
        LIST_INSERT(nxt->entry, now);
        LIST_INSERT(now->exit,nxt);
        if(tmp) BB_dfs(nxt);
    }
    if(last&&last->op==OP_GOTO){
        nxt=label_entry[last->x.label];
        bool tmp=LIST_EMPTY(nxt->entry);
        LIST_INSERT(nxt->entry, now);
        LIST_INSERT(now->exit,nxt);
        if(tmp) BB_dfs(nxt);
    }
    if(last&&last->op==OP_IF){
        nxt=label_entry[last->z.label];
        bool tmp=LIST_EMPTY(nxt->entry);
        LIST_INSERT(nxt->entry, now);
        LIST_INSERT(now->exit,nxt);
        if(tmp) BB_dfs(nxt);
    }
}

static void search(function * self){
    BB_dfs(LIST_GET(self->BBlist.begin,BB *));
    return;
}

static inline void pre_build(function * f){
    FOR_LIST(iter,f->BBlist){
        BB * bb=LIST_GET(iter, BB*);
        LIST_CLEAR(bb->entry);
        LIST_CLEAR(bb->exit);
    }
    search(f);
    return;
}

static inline void three2one(function * f,int o){
    pre_build(f);
    init(f,table[o].is_forward,table[o].boundary);
    build_map(f,table[o].is_forward,table[o].merge,table[o].statement);
    optimize_work(f,table[o].is_forward,table[o].statement,table[o].optimize);
    return;
}

void ir_optimize(){
    FOR_LIST(iter,function_list){
        function * f=LIST_GET(iter,function *);
        for(int _=0;_<3;_++){
            three2one(f,0);
            three2one(f,0);
            three2one(f,1);
            three2one(f,3);
            three2one(f,2);
            active_done=true;
            while(active_done){
                active_done=false;
                three2one(f,3);
            }    
        }
        pre_build(f);
    }
}